CWL.widgets.Box = function ( bm, name, attrs )
{
	attrs = dict ( attrs );

	this.bm  = bm;
	this.ctx = bm.ctx;
	this.name = name;
	
	this.x = attrs.get ( "x", 0 );
	this.y = attrs.get ( "y", 0 );
	this.w = attrs.get ( "w", 100 );
	this.h = attrs.get ( "h", 100 );

	this.step_x = attrs.get ( "step_x", 1 );
	this.step_y = attrs.get ( "step_y", 1 );

	this.x = Math.floor ( this.x / this.step_x ) * this.step_x;
	this.y = Math.floor ( this.y / this.step_y ) * this.step_y;
	this.w = Math.ceil ( this.w / this.step_x ) * this.step_x;
	this.h = Math.ceil ( this.h / this.step_y ) * this.step_y;

	this.dx = attrs.get ( "handle_width", 5 );
	this.dy = attrs.get ( "handle_height", 5 );
	this.info = attrs.get ( "info", dict () );

	this.mode = attrs.get ( "mode", 0 );

	this.is_draggable = ( this.mode & CWL.BOX_MODE_MOVE );
	this.is_resizable = ( this.mode & ( CWL.BOX_MODE_ALL ^ CWL.BOX_MODE_MOVE ) );

	if ( this.is_draggable || this.is_resizable )
		this.mouse_events = true;
	else
		this.mouse_events = false;

	this.em = new liwe.event_manager ( this, [ "render", "highlight", "clear", "handles", "resize", "move", "mousedown", "mouseup", "hide", "show", "lock", "unlock" ] );

/*
	this.cbacks = {
		"render" : function ( dbox ) { dbox.ctx.lineWidth = 2; dbox.ctx.strokeRect ( dbox.x, dbox.y, dbox.w, dbox.h ); },
		"highlight" : function ( dbox ) { dbox.ctx.fillStyle = "black"; dbox.ctx.fillRect ( dbox.x, dbox.y, dbox.w, dbox.h );},
		"clear" : function ( dbox ) { dbox.ctx.fillStyle = "white"; dbox.ctx.fillRect ( dbox.x, dbox.y, dbox.w, dbox.h ); },
		"handles" : function ( dbox ) { 
			dbox.ctx.fillStyle = "red"; 

			if ( dbox.mode & CWL.TOP_LEFT ) dbox.ctx.fillRect ( dbox.x, dbox.y, dbox.dx, dbox.dy );
			if ( dbox.mode & CWL.TOP_RIGHT ) dbox.ctx.fillRect ( dbox.x + dbox.w - dbox.dx, dbox.y, dbox.dx, dbox.dy );

			if ( dbox.mode & CWL.BOTTOM_LEFT )  dbox.ctx.fillRect ( dbox.x, dbox.y + dbox.h - dbox.dx, dbox.dx, dbox.dy );
			if ( dbox.mode & CWL.BOTTOM_RIGHT ) dbox.ctx.fillRect ( dbox.x + dbox.w - dbox.dx, dbox.y + dbox.h - dbox.dx, dbox.dx, dbox.dy );
		},
		"resize" : null,
		"move" : null
	};
*/

	this.status = CWL.VISIBLE;

	// this.mouse_events = true;
};

CWL.widgets.Box.prototype.resize = function ( w, h )
{
	if ( w < 0 ) w = 1;
	if ( h < 0 ) h = 1;

	this.w = w;
	this.h = h;

	this.dispatchEvent ( "resize" );
	// this.cbacks [ 'resize' ] && this.cbacks [ 'resize' ] ( this );
};

CWL.widgets.Box.prototype.render = function ( force )
{
	if ( force || this.is_visible () )
		this.dispatchEvent ( "render" );
};

CWL.widgets.Box.prototype.show = function ()
{
	this.status |= CWL.VISIBLE;

	this.dispatchEvent ( "show" );
};

CWL.widgets.Box.prototype.hide = function ()
{
	this.status &= ( 0xFF ^ CWL.VISIBLE );
	this.dispatchEvent ( "hide" );
};

CWL.widgets.Box.prototype.lock = function ()
{
	this.status |= CWL.LOCKED;
	this.dispatchEvent ( "lock" );
};

CWL.widgets.Box.prototype.unlock = function ()
{
	this.status &= ( 0xFF ^ CWL.LOCKED );
	this.dispatchEvent ( "unlock" );
};

CWL.widgets.Box.prototype.highlight = function ( do_highlight )
{
	if ( do_highlight )
	{
		this.dispatchEvent ( "highlight" );
		if ( this.is_resizable ) this.dispatchEvent ( "handles" );
	} else {
		this.clear ();
		this.bm.render ();
	}
};

CWL.widgets.Box.prototype.is_visible = function ()
{
	return !! ( this.status & CWL.VISIBLE );
};

CWL.widgets.Box.prototype.is_locked = function ()
{
	return !! ( this.status & CWL.LOCKED ); 
};

CWL.widgets.Box.prototype.clear = function ()
{
	this.dispatchEvent ( "clear" );
};

CWL.widgets.Box.prototype.in_box = function ( x, y )
{
	if ( this.is_locked ()  || ! ( this.is_visible () ) ) return false;

	return ( ( x >= this.x && x <= ( this.x + this.w ) ) && ( y >= this.y && y <= ( this.y + this.h ) ) );
};

/*
 *
 *     1 ---- 2 ---- 3
 *     |             |
 *     |             |
 *     4      9      5
 *     |             |
 *     |             |
 *     6 ---- 7 ---- 8
 *
 */
CWL.widgets.Box.prototype.get_mouse_zone = function ( x, y, dx, dy )
{
	if ( ! this.is_resizable ) return CWL.CENTER;

	var x1 = this.x + dx;
	var y1 = this.y + dy;
	var x2 = this.x + this.w - dx;
	var y2 = this.y + this.h - dy;

	if ( this.mode & CWL.TOP_LEFT ) 
		if ( ( x <= x1 ) && ( y <= y1 ) ) return CWL.TOP_LEFT;

	if ( this.mode & CWL.TOP_RIGHT ) 
		if ( ( x >= x2 ) && ( y <= y1 ) ) return CWL.TOP_RIGHT;

	if ( this.mode & CWL.BOTTOM_LEFT ) 
		if ( ( x <= x1 ) && ( y >= y2 ) ) return CWL.BOTTOM_LEFT;

	if ( this.mode & CWL.BOTTOM_RIGHT ) 
		if ( ( x >= x2 ) && ( y >= y2 ) ) return CWL.BOTTOM_RIGHT;

	if ( this.mode & CWL.TOP ) 
		if ( y <= y1 ) return CWL.TOP;

	if ( this.mode & CWL.LEFT ) 
		if ( x <= x1 ) return CWL.LEFT;

	if ( this.mode & CWL.RIGHT ) 
		if ( x >= x2 ) return CWL.RIGHT;

	if ( this.mode & CWL.BOTTOM ) 
		if ( y >= y2 ) return CWL.BOTTOM;

	if ( this.mode & CWL.CENTER ) 
		return CWL.CENTER;
};

CWL.widgets.BoxManager = function ( canvas )
{
	var self = this;

	this.canvas = canvas;
	this.ctx = canvas.getContext ( "2d" );

	this.handler_width  = 5;	// Dimensioni (in px) degli handler dei box
	this.handler_height = 5;

	// DEBUG
	this.ctx.strokeRect ( 0, 0, this.canvas.width, this.canvas.height );

	this._instances = dict ();
	this._mouse_events = list ();	// List of objects that can react to mouse events

	this._curr_dbox = null;

	liwe.events.add ( canvas, "mousemove", function ( e ) { self._mouse_move ( e ); } );
	liwe.events.add ( canvas, "mousedown", function ( e ) { self._mouse_down ( e ); } );
	liwe.events.add ( canvas, "mouseup",   function ( e ) { self._mouse_up   ( e ); } );
};

CWL.widgets.BoxManager.prototype.add = function ( name, attrs )
{
	var self = this;
	var dbox = new CWL.widgets.Box ( this, name, attrs );

	this._instances [ name ] = dbox;

	if ( dbox.mouse_events ) this._mouse_events.insert ( 0, dbox );

	dbox.addEventListener ( "show", function ( e ) { self._evt_redraw ( e ); }, true );
	dbox.addEventListener ( "hide", function ( e ) { self._evt_redraw ( e ); }, true );
	dbox.addEventListener ( "lock", function ( e ) { self._evt_redraw ( e ); }, true );
	dbox.addEventListener ( "unlock", function ( e ) { self._evt_redraw ( e ); }, true );

	return dbox;
};

CWL.widgets.BoxManager.prototype.del = function ( box )
{
	delete this._instances [ box.name ];
};

CWL.widgets.BoxManager.prototype.render = function ()
{
	this.ctx.clearRect ( 0, 0, this.canvas.width, this.canvas.height );

	this._instances.iterate ( function ( dbox )
	{
		dbox.render ();
	} );
};

CWL.widgets.BoxManager.prototype._check_mouse = function ( e )
{
	var found = false;
	var self = this;
	var x, y;

	x = this._get_rel_x ( e );
	y = this._get_rel_y ( e );

	this._mouse_events.iterate ( function ( dbox )
	{
		if ( found ) return;

		if ( dbox.in_box ( x, y ) )
		{
			self._curr_dbox = dbox;
			dbox.highlight ( true );
			found = true;
		}
	} );

	// if ( found ) this._do_dbox_action ( this._curr_dbox, x, y );

	if ( ! found ) 
	{
		this.canvas.style.cursor = "default";

		if ( this._curr_dbox ) this._curr_dbox.highlight ( false );
		this._curr_dbox = null;
	}
};

CWL.widgets.BoxManager.prototype._mouse_move = function ( e )
{
	this._curr_x = this._get_rel_x ( e );
	this._curr_y = this._get_rel_y ( e );

	if ( this._curr_dbox )
	{
		this.canvas.style.cursor = CWL.pointers.get ( this._curr_dbox.get_mouse_zone ( this._curr_x, this._curr_y, this.handler_width, this.handler_height ), "default" ); 

		if ( this._action )
		{
			this._curr_dbox.dispatchEvent ( 'move' );
		}
	}

	if ( ! this._action ) return this._check_mouse ( e );


	// this._action = this._curr_dbox.get_mouse_zone ( this._curr_x, this._curr_y, this.handler_width, this.handler_height );

	return this._do_dbox_action ( e );
};

CWL.widgets.BoxManager.prototype._mouse_down = function ( e )
{
	if ( ! this._curr_dbox ) return;

	this._curr_x = this._get_rel_x ( e );
	this._curr_y = this._get_rel_y ( e );

	this.ctx.fillColor = "#ff0000";
	this.ctx.fillRect ( this._curr_x -1, this._curr_y -1, 3, 3 );

	this._start_x = this._curr_x;
	this._start_y = this._curr_y;

	this._orig_x  = this._curr_dbox.x;
	this._orig_y  = this._curr_dbox.y;
	this._orig_w  = this._curr_dbox.w;
	this._orig_h  = this._curr_dbox.h;

	this._action = this._curr_dbox.get_mouse_zone ( this._curr_x, this._curr_y, this.handler_width, this.handler_height );

	this._curr_dbox.dispatchEvent ( "mousedown" );
};

CWL.widgets.BoxManager.prototype._mouse_up = function ( e )
{
	if ( ! this._curr_dbox ) return;

	this._action = 0;

	this._curr_dbox.dispatchEvent ( "mouseup" );
};


CWL.widgets.BoxManager.prototype._evt_redraw = function ()
{
	this.render ();
};

CWL.widgets.BoxManager.prototype._get_rel_x = function ( e )
{
	var rect = e.target.getBoundingClientRect ();
	return e.offsetX ? e.offsetX : e.clientX - rect.left;
};

CWL.widgets.BoxManager.prototype._get_rel_y = function ( e )
{
	var rect = e.target.getBoundingClientRect ();
	return e.offsetY ? e.offsetY : e.clientY - rect.top;
};

CWL.widgets.BoxManager.prototype._do_dbox_action = function ( e )
{
	var x = this._get_rel_x ( e );
	var y = this._get_rel_y ( e );
	var dx = ( x - this._curr_x );
	var dy = ( y - this._curr_y );
	var new_x, new_y, new_h, new_w;
	var px, py;

	px = ( this._curr_x - this._start_x );
	py = ( this._curr_y - this._start_y );


	var dbox = this._curr_dbox;

	this.canvas.style.cursor = CWL.pointers [ this._curr_dbox.get_mouse_zone ( this._curr_x, this._curr_y, this.handler_width, this.handler_height ) ];

	switch ( this._action )
	{
		case CWL.TOP_LEFT:
			dbox.x = Math.ceil ( ( this._orig_x + px ) / dbox.step_x ) * dbox.step_x;
			dbox.y = Math.ceil ( ( this._orig_y + py ) / dbox.step_y ) * dbox.step_y;

			new_w = Math.floor ( ( this._orig_w - px ) / dbox.step_x ) * dbox.step_x;
			new_h = Math.floor ( ( this._orig_h - py ) / dbox.step_y ) * dbox.step_y;

			dbox.resize ( new_w, new_h );
			break;

		case CWL.TOP:
			dbox.y = Math.ceil ( ( this._orig_y + py ) / dbox.step_y ) * dbox.step_y;
			new_h = Math.floor ( ( this._orig_h - py ) / dbox.step_y ) * dbox.step_y;

			dbox.resize ( dbox.w, new_h );
			break;

		case CWL.LEFT:
			dbox.x = Math.ceil ( ( this._orig_x + px ) / dbox.step_x ) * dbox.step_x;
			new_w = Math.floor ( ( this._orig_w - px ) / dbox.step_x ) * dbox.step_x;
			dbox.resize ( new_w, dbox.h );
			break;

		case CWL.TOP_RIGHT:
			dbox.y = Math.ceil ( ( this._orig_y + py ) / dbox.step_y ) * dbox.step_y;
			new_h = Math.floor ( ( this._orig_h - py ) / dbox.step_y ) * dbox.step_y;

			new_w = Math.floor ( ( this._orig_w + px ) / dbox.step_x ) * dbox.step_x;
			dbox.resize ( new_w, new_h );
			break;

		case CWL.RIGHT:
			new_w = Math.floor ( ( this._orig_w + px ) / dbox.step_x ) * dbox.step_x;
			dbox.resize ( new_w, dbox.h );
			break;

		case CWL.BOTTOM_LEFT:
			dbox.x = Math.ceil ( ( this._orig_x + px ) / dbox.step_x ) * dbox.step_x;
			new_w = Math.floor ( ( this._orig_w - px ) / dbox.step_x ) * dbox.step_x;
			new_h = Math.floor ( ( this._orig_h + py ) / dbox.step_y ) * dbox.step_y;
			dbox.resize ( new_w, new_h );
			break;

		case CWL.BOTTOM:
			new_h = Math.floor ( ( this._orig_h + py ) / dbox.step_y ) * dbox.step_y;
			dbox.resize ( dbox.w, new_h );
			break;

		case CWL.BOTTOM_RIGHT:
			new_w = Math.floor ( ( this._orig_w + px ) / dbox.step_x ) * dbox.step_x;
			new_h = Math.floor ( ( this._orig_h + py ) / dbox.step_y ) * dbox.step_y;
			dbox.resize ( new_w, new_h ); // dbox.h + dy );
			break;

		case CWL.CENTER:
			dbox.x = Math.floor ( ( this._orig_x + px ) / dbox.step_x ) * dbox.step_x;
			dbox.y = Math.floor ( ( this._orig_y + py ) / dbox.step_y ) * dbox.step_y;

			break;
	}

	this._curr_x = x;
	this._curr_y = y;

	this.render ();
};

CWL.prototype.BoxManager = function ()
{
	var bm = new CWL.widgets.BoxManager ( this.canvas );

	this.instances [ "box-man" ] = bm;

	return bm;
};

